﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Input;
using System.Xml;
using System.IO;
using Newtonsoft.Json;

namespace RhythmGame
{
    public class Settings
    {
        private string playerName = "新玩家"; // 玩家名称
        private string currentSongName = "HOYO-MiX feat. NIDA - Proi Proi 拂晓"; // 当前歌曲名称
        private int winWidth = 1080; // 窗口宽度
        private int winHeight = 720; // 窗口高度
        private int unitConversion = 10; // 单位转换参数
        private int volume = 100; // 音量
        private int videoBrightness = 100; // 视频亮度
        private int fps = 60; // FPS 帧率
        private int dropSpeed = 12; // 物块（音符）下落速度
        private int offset = 0; // 偏移量 ms
        private int judgeLineHeight = 2; // 判定线高度
        private int judgeHeight; // 从顶端至判定线的距离
        private int duringTime; // 物块持续时间
        private int judgeLineSize = 4; // 判定线宽度（粗细）
        private int maxKeysQuantity = 10; // 最大键位数量
        private int scoringMethod = 3; // 计分方法
        private Color backgroundColor = Color.FromArgb(0, 0, 0); // 背景颜色
        private Color blocksColor = Color.SkyBlue; // 物块颜色
        private Color breakBlocksColor = Color.OrangeRed; // Break 物块颜色
        private Color dragsColor = Color.Yellow; // Drag 物块颜色
        private Color protectedBroderColor = Color.White; // Protected 物块边框颜色
        private int protectedBorderPanding = 2; // Protected 物块边框粗细
        private Color judgeLineColor = Color.FromArgb(25, 210, 255); // 判定线颜色
        private Color strikeColor = Color.Orange; // 击打效果颜色

        private Color[] judgeTextColor = new Color[] 
        {
            // 0: CriticalPerfect, 1: Perfect, 2: Great, 3: Good, 4: Miss
            Color.Yellow, Color.Orange, 
            Color.DeepPink, Color.Green,
            Color.DarkGray
        }; // 判定文字颜色
        private Font judgeTextFont = new Font("Arial", 12, FontStyle.Bold); // 判定文字字体
        private Font textFont = new Font("微软雅黑", 16, FontStyle.Regular); // 文字字体
        private Font buttonTextFont = new Font("微软雅黑", 20, FontStyle.Regular); // 按钮文字字体

        private bool isTop = false; // 是否置顶
        private bool setVideoVisible = true; // 是否显示视频
        private bool setTracksDividingLineVisible = true; // 是否显示轨道分割线
        private List<Keys> keysLocate; // 键位

        // 判定标准
        internal enum JudgeStandard : int
        {
            CriticalPerfect = 25,
            Perfect = 50,
            Great = 75,
            Good = 100
            // 大于 100 则为 Miss
        }

        // 释放判定标准（用于 Hold 及类似音符）
        internal enum ReleaseJudgeStandard : int
        {
            CriticalPerfect = 25,
            Perfect = 50,
            Great = 75,
            Good = 100
            // 大于 100 则为 Miss
        }

        private static readonly Settings singleInstance = new Settings(); // 单例模式

        // 受保护构造函数
        protected Settings()
        {
            judgeHeight = (int)(winHeight * (1 - (double)judgeLineHeight / 20));
            duringTime = judgeHeight / dropSpeed * unitConversion;
            keysLocate = new List<Keys>(new Keys[maxKeysQuantity]);
        }

        public static Settings Instance
        {
            get
            {
                return singleInstance;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("Settings instance cannot be set to null.");
                }
            }
        }

        public List<Keys> KeysInit()
        {
            List<Keys> keysL = new List<Keys>(new Keys[maxKeysQuantity]);
            keysL[0] = Keys.A;
            keysL[1] = Keys.S;
            keysL[2] = Keys.E;
            keysL[3] = Keys.Q;
            keysL[4] = Keys.W;
            keysL[5] = Keys.OemOpenBrackets;
            keysL[6] = Keys.OemCloseBrackets;
            keysL[7] = Keys.I;
            keysL[8] = Keys.O;
            keysL[9] = Keys.P;
            return keysL;
        }
        
        public void KeysSet(List<Keys> keys)
        {
            keysLocate = keys;
        }

        // 玩家名称
        public string PlayerName
        {
            get => playerName;
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    throw new ArgumentException("PlayerName cannot be null or empty.");
                }
                else if (value != playerName)
                {
                    playerName = value;
                }
            }
        }

        // 当前歌曲名称
        public string CurrentSongName
        {
            get => currentSongName;
            set
            {
                if (value != currentSongName)
                {
                    currentSongName = value;
                }
            }
        }

        // 窗口宽度
        public int WinWidth
        {
            get => winWidth;
            set
            {
                if (value <= 0)
                {
                    throw new ArgumentOutOfRangeException("WinWidth must be greater than 0.");
                }
                else if (value != winWidth)
                {
                    winWidth = value;
                }
            }
        }

        // 窗口高度
        public int WinHeight
        {
            get => winHeight;
            set
            {
                if (value <= 0)
                {
                    throw new ArgumentOutOfRangeException("WinHeight must be greater than 0.");
                }
                else if (value != winHeight)
                {
                    winHeight = value;
                }
            }
        }

        // 单位转换
        public int UnitConversion
        {
            get => unitConversion;
            set
            {
                if (value <= 0)
                {
                    throw new ArgumentOutOfRangeException("UnitConversion must be greater than 0.");
                }
                else if (value != unitConversion)
                {
                    unitConversion = value;
                }
            }
        }

        // 音量
        public int Volume
        {
            get => volume;
            set
            {
                if (value < 0 || value > 100)
                {
                    throw new ArgumentOutOfRangeException("Volume must be between 0 and 100.");
                }
                else if (value != volume)
                {
                    volume = value;
                }
            }
        }

        // 视频亮度
        public int VideoBrightness
        {
            get => videoBrightness;
            set
            {
                if (value < 0 || value > 100)
                {
                    throw new ArgumentOutOfRangeException("VideoBrightness must be between 0 and 100.");
                }
                else if (value != videoBrightness)
                {
                    videoBrightness = value;
                }
            }
        }

        // fps
        public int FPS
        {
            get => fps;
            set
            {
                if (value < 0)
                {
                    throw new ArgumentOutOfRangeException("FPS must be greater than 0.");
                }
                else if (value != fps)
                {
                    fps = value;
                }
            }
        }

        // 下落速度
        public int DropSpeed
        {
            get => dropSpeed;
            set
            {
                if (value <= 0)
                {
                    throw new ArgumentOutOfRangeException("DropSpeed must be greater than 0.");
                }
                else if (value != dropSpeed)
                {
                    dropSpeed = value;
                }
            }
        }

        // 偏移 ms
        public int Offset
        {
            get => offset;
            set
            {
                if (value != offset)
                {
                    offset = value;
                }
            }
        }

        // 物块持续时间 (从出现到判定线位置的持续时间)
        public int DuringTime
        {
            get => duringTime;
            set
            {
                if (value <= 0)
                {
                    throw new ArgumentOutOfRangeException("DuringTime must be greater than 0.");
                }
                else if (value != duringTime)
                {
                    duringTime = value;
                }
            }
        }

        // 判定线高度
        public int JudgeLineHeight
        {
            get => judgeLineHeight;
            set
            {
                if (value <= 0 || value > 10)
                {
                    throw new ArgumentOutOfRangeException("JudgeLineHeight must be greater than 0.");
                }
                else if (value != judgeLineHeight)
                {
                    judgeLineHeight = value;
                }
            }
        }

        // 从顶端至判定线的距离
        public int JudgeHeight
        {
            get => judgeHeight;
            set
            {
                if (value != judgeHeight)
                {
                    judgeHeight = value;
                }
            }
        }

        // 判定线宽度
        public int JudgeLineSize
        {
            get => judgeLineSize;
            set
            {
                if (value <= 0 || value > 10)
                {
                    throw new ArgumentOutOfRangeException("JudgeLineSize must be greater than 0.");
                }
                else if (value != judgeLineSize)
                {
                    judgeLineSize = value;
                }
            }
        }

        // 最大键位数量
        public int MaxKeysQuantity
        {
            get => maxKeysQuantity;
            set
            {
                if (value <= 0)
                {
                    throw new ArgumentOutOfRangeException("MaxKeysQuantity must be greater than 0.");
                }
                else if (value != maxKeysQuantity)
                {
                    maxKeysQuantity = value;
                }
            }
        }

        // 计分方法
        public int ScoringMethod
        {
            /* 计分方式：
             * 1: 准确度从 100% 开始，
             *      判定为 Great 及以下，则准确度会酌情降低，判定为 Perfect 准确度不变；
             *      对于 Break 物块，若判定为 Critical Perfect，则准确度会提高(总提高数量不会超过 1%)，其他判定与非 Break 物块一致。
             * 2: 从 0% 开始，
             *      判定不是 Miss，准确度会酌情增加。
             * 3: 从 101% 开始 (若谱面中无 Break 物块，则使用 1 的计分方式)，
             *      对于非 Break 物块，判定为 Great 及以下，则准确度会酌情降低，判定为 Perfect 及以上准确度不变；
             *      对于 Break 物块，若判定为 Critical Perfect，则准确度不变，否则准确度酌情降低。
             * 注：以上几种方式仅在游戏中的准确度显示不同，游戏后最终准确度相同。
             * 最低准确度为 0%。若谱面中无 Break 物块，最高准确度为 100%；否则为 101%。
             * 全部为 Miss 时，准确度最低。所有非 Break 物块均为 Perfect 及以上，Break 物块均为 Critical Perfect 时，准确度为 101%。
            **/
            get => scoringMethod;
            set
            {
                if (value != scoringMethod)
                {
                    scoringMethod = value;
                }
            }
        }

        // 背景颜色
        public Color BackgroundColor
        {
            get => backgroundColor;
            set
            {
                if (value != backgroundColor)
                {
                    backgroundColor = value;
                }
            }
        }

        // 物块颜色
        public Color BlocksColor
        {
            get => blocksColor;
            set
            {
                if (value != blocksColor)
                {
                    blocksColor = value;
                }
            }
        }

        // Break 物块颜色
        public Color BreakBlocksColor
        {
            get => breakBlocksColor;
            set
            {
                if (value != breakBlocksColor)
                {
                    breakBlocksColor = value;
                }
            }
        }

        // Drag 物块颜色
        public Color DragsColor
        {
            get => dragsColor;
            set
            {
                if (value != dragsColor)
                {
                    dragsColor = value;
                }
            }
        }

        // Protected 物块边框颜色
        public Color ProtectedBroderColor
        {
            get => protectedBroderColor;
            set
            {
                if (value != protectedBroderColor)
                {
                    protectedBroderColor = value;
                }
            }
        }

        // Protected 物块边框粗细
        public int ProtectedBorderPanding
        {
            get => protectedBorderPanding;
            set
            {
                if (value <= 0)
                {
                    throw new ArgumentOutOfRangeException("ProtectedBorderPanding must be greater than 0.");
                }
                else if (value != protectedBorderPanding)
                {
                    protectedBorderPanding = value;
                }
            }
        }

        // 判定线颜色
        public Color JudgeLineColor
        {
            get => judgeLineColor;
            set
            {
                if (value != judgeLineColor)
                {
                    judgeLineColor = value;
                }
            }
        }

        // 击打效果颜色
        public Color StrikeColor
        {
            get => strikeColor;
            set
            {
                if (value != strikeColor)
                {
                    strikeColor = value;
                }
            }
        }

        // 判定文字颜色
        public Color[] JudgeTextColor
        {
            get => judgeTextColor;
            set
            {
                if (value != judgeTextColor)
                {
                    judgeTextColor = value;
                }
            }
        }

        // 判定文字字体
        public Font JudgeTextFont
        {
            get => judgeTextFont;
            set
            {
                if (value != judgeTextFont)
                {
                    judgeTextFont = value;
                }
            }
        }

        // 文字字体
        public Font TextFont
        {
            get => textFont;
            set
            {
                if (value != textFont)
                {
                    textFont = value;
                }
            }
        }

        // 按钮文字字体
        public Font ButtonTextFont
        {
            get => buttonTextFont;
            set
            {
                if (value != buttonTextFont)
                {
                    buttonTextFont = value;
                }
            }
        }

        // 是否置顶
        public bool IsTop
        {
            get => isTop;
            set
            {
                if (value != isTop)
                {
                    isTop = value;
                }
            }
        }

        // 是否显示视频
        public bool SetVideoVisible
        {
            get => setVideoVisible;
            set
            {
                if (value != setVideoVisible)
                {
                    setVideoVisible = value;
                }
            }
        }

        // 是否显示轨道分割线
        public bool SetTracksDividingLineVisible
        {
            get => setTracksDividingLineVisible;
            set
            {
                if (value != setTracksDividingLineVisible)
                {
                    setTracksDividingLineVisible = value;
                }
            }
        }

        // 键位
        public List<Keys> KeysLocate
        {
            get => KeysInit();
            set
            {
                if (value != keysLocate)
                {
                    keysLocate = value;
                }
            }
        }

        // DTO类，仅用于序列化
        private class SettingsDto
        {
            public string PlayerName { get; set; }
            public string CurrentSongName { get; set; }
            public int WinWidth { get; set; }
            public int WinHeight { get; set; }
            public int UnitConversion { get; set; }
            public int Volume { get; set; }
            public int VideoBrightness { get; set; }
            public int FPS { get; set; }
            public int DropSpeed { get; set; }
            public int Offset { get; set; }
            public int JudgeLineHeight { get; set; }
            public int JudgeHeight { get; set; }
            public int DuringTime { get; set; }
            public int JudgeLineSize { get; set; }
            public int MaxKeysQuantity { get; set; }
            public int ScoringMethod { get; set; }
            public string BackgroundColor { get; set; }
            public string BlocksColor { get; set; }
            public string BreakBlocksColor { get; set; }
            public string DragsColor { get; set; }
            public string ProtectedBroderColor { get; set; }
            public int ProtectedBorderPanding { get; set; }
            public string JudgeLineColor { get; set; }
            public string StrikeColor { get; set; }
            public List<string> JudgeTextColor { get; set; }
            public string JudgeTextFont { get; set; }
            public string TextFont { get; set; }
            public string ButtonTextFont { get; set; }
            public bool IsTop { get; set; }
            public bool SetVideoVisible { get; set; }
            public bool SetTracksDividingLineVisible { get; set; }
            public List<int> KeysLocate { get; set; }
        }

        private static string GetConfigPath()
        {
            string exePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
            string dir = Path.GetDirectoryName(exePath);
            return Path.Combine(dir, "settings.json");
        }

        public void Save()
        {
            var dto = new SettingsDto
            {
                PlayerName = this.PlayerName,
                CurrentSongName = this.CurrentSongName,
                WinWidth = this.WinWidth,
                WinHeight = this.WinHeight,
                UnitConversion = this.UnitConversion,
                Volume = this.Volume,
                VideoBrightness = this.VideoBrightness,
                FPS = this.FPS,
                DropSpeed = this.DropSpeed,
                Offset = this.Offset,
                JudgeLineHeight = this.JudgeLineHeight,
                JudgeHeight = this.JudgeHeight,
                DuringTime = this.DuringTime,
                JudgeLineSize = this.JudgeLineSize,
                MaxKeysQuantity = this.MaxKeysQuantity,
                ScoringMethod = this.ScoringMethod,
                BackgroundColor = ColorTranslator.ToHtml(this.BackgroundColor),
                BlocksColor = ColorTranslator.ToHtml(this.BlocksColor),
                BreakBlocksColor = ColorTranslator.ToHtml(this.BreakBlocksColor),
                DragsColor = ColorTranslator.ToHtml(this.DragsColor),
                ProtectedBroderColor = ColorTranslator.ToHtml(this.ProtectedBroderColor),
                ProtectedBorderPanding = this.ProtectedBorderPanding,
                JudgeLineColor = ColorTranslator.ToHtml(this.JudgeLineColor),
                StrikeColor = ColorTranslator.ToHtml(this.StrikeColor),
                JudgeTextColor = new List<string>(Array.ConvertAll(this.JudgeTextColor, c => ColorTranslator.ToHtml(c))),
                JudgeTextFont = FontToString(this.JudgeTextFont),
                TextFont = FontToString(this.TextFont),
                ButtonTextFont = FontToString(this.ButtonTextFont),
                IsTop = this.IsTop,
                SetVideoVisible = this.SetVideoVisible,
                SetTracksDividingLineVisible = this.SetTracksDividingLineVisible,
                KeysLocate = this.KeysLocate.ConvertAll(k => (int)k)
            };
            string json = JsonConvert.SerializeObject(dto, Newtonsoft.Json.Formatting.Indented);
            File.WriteAllText(GetConfigPath(), json);
        }

        public void Load()
        {
            string path = GetConfigPath();
            if (!File.Exists(path))
            {
                // 文件不存在，使用默认设置并保存
                this.Save();
                return;
            }
            string json = File.ReadAllText(path);
            var dto = JsonConvert.DeserializeObject<SettingsDto>(json);
            if (dto == null) return;

            this.WinWidth = dto.WinWidth;
            this.WinHeight = dto.WinHeight;
            this.UnitConversion = dto.UnitConversion;
            this.Volume = dto.Volume;
            this.VideoBrightness = dto.VideoBrightness;
            this.FPS = dto.FPS;
            this.DropSpeed = dto.DropSpeed;
            this.Offset = dto.Offset;
            this.JudgeLineHeight = dto.JudgeLineHeight;
            this.JudgeHeight = dto.JudgeHeight;
            this.DuringTime = dto.DuringTime;
            this.JudgeLineSize = dto.JudgeLineSize;
            this.MaxKeysQuantity = dto.MaxKeysQuantity;
            this.ScoringMethod = dto.ScoringMethod;
            this.BackgroundColor = ColorTranslator.FromHtml(dto.BackgroundColor);
            this.BlocksColor = ColorTranslator.FromHtml(dto.BlocksColor);
            this.BreakBlocksColor = ColorTranslator.FromHtml(dto.BreakBlocksColor);
            this.DragsColor = ColorTranslator.FromHtml(dto.DragsColor);
            this.ProtectedBroderColor = ColorTranslator.FromHtml(dto.ProtectedBroderColor);
            this.ProtectedBorderPanding = dto.ProtectedBorderPanding;
            this.JudgeLineColor = ColorTranslator.FromHtml(dto.JudgeLineColor);
            this.StrikeColor = ColorTranslator.FromHtml(dto.StrikeColor);
            this.JudgeTextColor = dto.JudgeTextColor.ConvertAll(ColorTranslator.FromHtml).ToArray();
            this.JudgeTextFont = StringToFont(dto.JudgeTextFont);
            this.TextFont = StringToFont(dto.TextFont);
            this.ButtonTextFont = StringToFont(dto.ButtonTextFont);
            this.IsTop = dto.IsTop;
            this.SetVideoVisible = dto.SetVideoVisible;
            this.SetTracksDividingLineVisible = dto.SetTracksDividingLineVisible;
            this.KeysLocate = dto.KeysLocate.ConvertAll(i => (Keys)i);
        }

        private static string FontToString(Font font)
        {
            return $"{font.FontFamily.Name},{font.Size},{(int)font.Style}";
        }

        private static Font StringToFont(string str)
        {
            var parts = str.Split(',');
            if (parts.Length != 3) return new Font("微软雅黑", 12, FontStyle.Regular);
            string name = parts[0];
            float size = float.TryParse(parts[1], out var s) ? s : 12;
            FontStyle style = (FontStyle)int.Parse(parts[2]);
            return new Font(name, size, style);
        }
    }
}
